#include "U2Root.h"

#include "U2DynLib.h"
#include "U2IPlugin.h"
#include "U2DynLibManager.h"
#include "U2Exception.h"
#include "U2Log.h"
#include "U2LogManager.h"
#include "U2StringUtil.h"
#include "U2StringConverter.h"
#include "U2ArchiveManager.h"
#include "U2ArchiveFile.h"
#include "U2ResourceGroupManager.h"
#include "U2ResourceBackgroundQueue.h"
#include "U2RenderSystem.h"
#include "U2Timer.h"
#include "U2ConfigFile.h"
#include "U2WindowEventUtilities.h"
#include "U2HardwareBufferManager.h"
#include "Threading/U2DefaultWorkQueue.h"
#include "U2RenderSystemCapabilitiesManager.h"
#include "U2RenderWindow.h"
#include "U2CodecFreeImage.h"
#include "U2ControllerManager.h"


#if U2_PLATFORM == U2_PLATFORM_ANDROID
#   include "U2LogListenerAndroid.h"
#endif

#if U2_PLATFORM == U2_PLATFORM_APPLE_IOS
#   include "macUtils.h"
#endif




U2EG_NAMESPACE_USING


typedef void (*DLL_START_PLUGIN)(void);
typedef void (*DLL_STOP_PLUGIN)(void);


//-----------------------------------------------------------------------
template<> U2Root* U2Singleton<U2Root>::s_pSingleton = 0;

U2Root* U2Root::getSingletonPtr(void)
{
    return s_pSingleton;
}

U2Root& U2Root::getSingleton(void)
{
    assert( s_pSingleton );
    return ( *s_pSingleton );
}

//-----------------------------------------------------------------------
U2Root::U2Root(const U2String& pluginFileName, const U2String& configFileName, 
           const U2String& logFileName)
: m_bInitialised(false)
, mActiveRenderer(0)
, mAutoWindow(0)
, mFrameSmoothingTime(0.0f)
, mNextFrame(0)
, mFirstTimePostWindowInit(false)
{
	m_szVersion = U2StringConverter::toString(U2_VERSION_MAJOR)
		+ "."
		+ U2StringConverter::toString(U2_VERSION_MINOR)
		+ "."
		+ U2StringConverter::toString(U2_VERSION_PATCH)
		+ U2_VERSION_SUFFIX
		+ " ("
		+ U2_VERSION_NAME
		+ ")";

    // Create log manager and default log file if there is no log manager yet
    if (U2LogManager::getSingletonPtr() == 0)
    {
        mLogManager = U2_NEW U2LogManager();
        U2Log *l = mLogManager->createLog(logFileName, true, true);
#if U2_PLATFORM == U2_PLATFORM_ANDROID
        U2LogListenerAndroid *g_ll = U2_NEW U2LogListenerAndroid();
        l->addListener(g_ll);
#endif
    }

	U2LogManager::getSingleton().logMessage("*-*-* U2 Initialising");
	U2LogManager::getSingleton().logMessage("*-*-* Version: " + m_szVersion);
	U2LogManager::getSingleton().logMessage("*-*-* Platform: " + U2String(U2_PLATFORM_NAME));

    mConfigFileName = configFileName;

    // Dynlib
    mDynLibManager              = U2_NEW U2DynLibManager();

    // Archive
    mArchiveManager             = U2_NEW U2ArchiveManager();
    mArchiveFactoryFileSystem   = U2_NEW U2ArchiveFactoryFile();
    U2ArchiveManager::getSingleton().addArchiveFactory( mArchiveFactoryFileSystem );
#if U2_NO_ZIP_ARCHIVE == 0
    mZipArchiveFactory          = U2_NEW ZipArchiveFactory();
    U2ArchiveManager::getSingleton().addArchiveFactory( mZipArchiveFactory );
    mEmbeddedZipArchiveFactory  = U2_NEW EmbeddedZipArchiveFactory();
    U2ArchiveManager::getSingleton().addArchiveFactory( mEmbeddedZipArchiveFactory );
#endif


    // Register image codecs
    U2CodecFreeImage::startup();


    // ResourceGroupManager
    mResourceGroupManager       = U2_NEW U2ResourceGroupManager();
    //U2ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

    // Create SceneManager enumerator (note - will be managed by singleton)
    mSceneManagerEnum = U2_NEW U2SceneManagerEnumerator();

    mTimer = U2_NEW U2Timer();

    // WorkQueue (note: users can replace this if they want)
    U2DefaultWorkQueue* defaultQ = U2_NEW U2DefaultWorkQueue("U2Root");
    // never process responses in main thread for longer than 10ms by default
    defaultQ->setResponseProcessingTimeLimit(10);
    // match threads to hardware
#if U2_THREAD_SUPPORT
    unsigned threadCount = U2_THREAD_HARDWARE_CONCURRENCY;
    if (!threadCount)
        threadCount = 1;
    defaultQ->setWorkerThreadCount(threadCount);
#endif
    // only allow workers to access rendersystem if threadsupport is 1
#if U2_THREAD_SUPPORT == 1
    defaultQ->setWorkersCanAccessRenderSystem(true);
#else
    defaultQ->setWorkersCanAccessRenderSystem(false);
#endif
    mWorkQueue = defaultQ;

    // ResourceBackgroundQueue
    mResourceBackgroundQueue = U2_NEW U2ResourceBackgroundQueue();

    mRenderSystemCapabilitiesManager = U2_NEW U2RenderSystemCapabilitiesManager();

    // Load plugins
    if (!pluginFileName.empty())
        loadPlugins(pluginFileName);

    mControllerManager = NULL;

}

U2Root::~U2Root(void)
{
    U2_DELETE mSceneManagerEnum;
    U2_DELETE mDynLibManager;

    U2_DELETE mArchiveManager;
    U2_DELETE mArchiveFactoryFileSystem;
#if U2_NO_ZIP_ARCHIVE == 0
    U2_DELETE mZipArchiveFactory;
    U2_DELETE mEmbeddedZipArchiveFactory;
#endif

    if( mControllerManager )
        U2_DELETE mControllerManager;

    U2_DELETE mResourceBackgroundQueue;
    U2_DELETE mResourceGroupManager;
    U2_DELETE mRenderSystemCapabilitiesManager;
    

    mFirstTimePostWindowInit = false;
}


void U2Root::loadPlugin(const U2String& pluginName)
{
	// Load plugin library
	U2DynLib* pLib = U2DynLibManager::getSingleton().load( pluginName );
	// Store for later unload
	// Check for existence, because if called 2+ times U2DynLibManager returns existing entry
	if (std::find(m_pluginLibs.begin(), m_pluginLibs.end(), pLib) == m_pluginLibs.end())
	{
		m_pluginLibs.push_back(pLib);

		// Call startup function
		DLL_START_PLUGIN pFunc = (DLL_START_PLUGIN)pLib->getSymbol("dllStartPlugin");

		if (!pFunc)
		{
			U2_EXCEPT(U2Exception::ERR_ITEM_NOT_FOUND, "Cannot find symbol dllStartPlugin in library " + pluginName,
			"U2Root::loadPlugin");
		}

		// This must call installPlugin
		pFunc();
	}
}

void U2Root::unloadPlugin(const U2String& pluginName)
{
	PluginLibList::iterator i;

	for (i = m_pluginLibs.begin(); i != m_pluginLibs.end(); ++i)
	{
		if ((*i)->getName() == pluginName)
		{
			// Call plugin shutdown
			DLL_STOP_PLUGIN pFunc = (DLL_STOP_PLUGIN)(*i)->getSymbol("dllStopPlugin");
			// this must call uninstallPlugin
			pFunc();
			// Unload library (destroyed by U2DynLibManager)
			U2DynLibManager::getSingleton().unload(*i);
			m_pluginLibs.erase(i);
			return;
		}
	}
}

void U2Root::installPlugin(U2IPlugin* pPlugin)
{
	U2LogManager::getSingleton().logMessage("Installing plugin: " + pPlugin->getName());

	m_plugins.push_back(pPlugin);
	pPlugin->install();

	// if rendersystem is already initialised, call rendersystem init too
	if (m_bInitialised)
	{
		pPlugin->initialise();
	}

	U2LogManager::getSingleton().logMessage("Plugin successfully installed");
}

void U2Root::uninstallPlugin(U2IPlugin* pPlugin)
{
	U2LogManager::getSingleton().logMessage("Uninstalling plugin: " + pPlugin->getName());
	PluginInstanceList::iterator i = 
		std::find(m_plugins.begin(), m_plugins.end(), pPlugin);
	if (i != m_plugins.end())
	{
		if (m_bInitialised)
			pPlugin->shutdown();
		pPlugin->uninstall();
		m_plugins.erase(i);
	}
	U2LogManager::getSingleton().logMessage("Plugin successfully uninstalled");
}

//-----------------------------------------------------------------------
void U2Root::loadPlugins( const U2String& pluginsfile )
{
    U2StringVector pluginList;
    U2String pluginDir;
    U2ConfigFile cfg;

    try
    {
        cfg.load( pluginsfile );
    }
    catch (U2Exception)
    {
        U2LogManager::getSingleton().logMessage(pluginsfile + " not found, automatic plugin loading disabled.");
        return;
    }

    pluginDir = cfg.getSetting("PluginFolder"); // Ignored on Mac OS X, uses Resources/ directory
    pluginList = cfg.getMultiSetting("Plugin");

    if (!pluginDir.empty() && *pluginDir.rbegin() != '/' && *pluginDir.rbegin() != '\\')
    {
#if U2_PLATFORM == U2_PLATFORM_WIN32
        pluginDir += "\\";
#elif U2_PLATFORM == U2_PLATFORM_LINUX
        pluginDir += "/";
#endif
    }

    for( U2StringVector::iterator it = pluginList.begin(); it != pluginList.end(); ++it )
    {
        loadPlugin(pluginDir + (*it));
    }
}
//-----------------------------------------------------------------------
void U2Root::shutdownPlugins(void)
{
    // NB Shutdown plugins in reverse order to enforce dependencies
    for (PluginInstanceList::reverse_iterator i = mPlugins.rbegin(); i != mPlugins.rend(); ++i)
    {
        (*i)->shutdown();
    }
}
//-----------------------------------------------------------------------
void U2Root::initialisePlugins(void)
{
    for (PluginInstanceList::iterator i = mPlugins.begin(); i != mPlugins.end(); ++i)
    {
        (*i)->initialise();
    }
}
//-----------------------------------------------------------------------
void U2Root::unloadPlugins(void)
{
    // unload dynamic libs first
    for (PluginLibList::reverse_iterator i = mPluginLibs.rbegin(); i != mPluginLibs.rend(); ++i)
    {
        // Call plugin shutdown
        DLL_STOP_PLUGIN pFunc = (DLL_STOP_PLUGIN)(*i)->getSymbol("dllStopPlugin");
        // this will call uninstallPlugin
        pFunc();
        // Unload library & destroy
        U2DynLibManager::getSingleton().unload(*i);

    }
    mPluginLibs.clear();

    // now deal with any remaining plugins that were registered through other means
    for (PluginInstanceList::reverse_iterator i = mPlugins.rbegin(); i != mPlugins.rend(); ++i)
    {
        // Note this does NOT call uninstallPlugin - this shutdown is for the 
        // detail objects
        (*i)->uninstall();
    }
    mPlugins.clear();
}

//-----------------------------------------------------------------------
void U2Root::saveConfig(void)
{
#if U2_PLATFORM == U2_PLATFORM_APPLE_IOS
    // Check the Documents directory within the application sandbox
    U2String outBaseName, extension, configFileName;
    U2StringUtil::splitFilename(mConfigFileName, outBaseName, extension);
    configFileName = macBundlePath() + "/../Documents/" + outBaseName;
    std::ofstream of(configFileName.c_str());
    if (of.is_open())
        mConfigFileName = configFileName;
    else
        mConfigFileName.clear();
#else
    if (mConfigFileName.empty())
        return;

    std::ofstream of(mConfigFileName.c_str());
#endif
    if (!of)
        U2_EXCEPT(U2Exception::ERR_CANNOT_WRITE_TO_FILE, "Cannot create settings file.",
        "U2Root::saveConfig");

    if (mActiveRenderer)
    {
        of << "Render System=" << mActiveRenderer->getName() << std::endl;
    }
    else
    {
        of << "Render System=" << std::endl;
    }

    for (U2RenderSystemList::const_iterator pRend = getAvailableRenderers().begin(); pRend != getAvailableRenderers().end(); ++pRend)
    {
        U2RenderSystem* rs = *pRend;
        of << std::endl;
        of << "[" << rs->getName() << "]" << std::endl;
        const U2ConfigOptionMap& opts = rs->getConfigOptions();
        for (U2ConfigOptionMap::const_iterator pOpt = opts.begin(); pOpt != opts.end(); ++pOpt)
        {
            of << pOpt->first << "=" << pOpt->second.currentValue << std::endl;
        }
    }

    of.close();
}
//-----------------------------------------------------------------------
bool U2Root::restoreConfig(void)
{
#if U2_PLATFORM == U2_PLATFORM_APPLE_IOS
    // Read the config from Documents first(user config) if it exists on iPhone.
    // If it doesn't exist or is invalid then use mConfigFileName

    U2String outBaseName, extension, configFileName;
    U2StringUtil::splitFilename(mConfigFileName, outBaseName, extension);
    configFileName = macBundlePath() + "/../Documents/" + outBaseName;

    std::ifstream fp;
    fp.open(configFileName.c_str(), std::ios::in);
    if(fp.is_open())
    {
        // A config file exists in the users Documents dir, we'll use it
        mConfigFileName = configFileName;
    }
    else
    {
        std::ifstream configFp;

        // This might be the first run because there is no config file in the
        // Documents directory.  It could also mean that a config file isn't being used at all

        // Try the path passed into initialise
        configFp.open(mConfigFileName.c_str(), std::ios::in);

        // If we can't open this file then we have no default config file to work with
        // Use the documents dir then. 
        if(!configFp.is_open())
        {
            // Check to see if one was included in the app bundle
            mConfigFileName = macBundlePath() + "/u2.cfg";

            configFp.open(mConfigFileName.c_str(), std::ios::in);

            // If we can't open this file then we have no default config file to work with
            // Use the Documents dir then. 
            if(!configFp.is_open())
                mConfigFileName = configFileName;
        }

        configFp.close();
    }

    fp.close();
#endif

    if (mConfigFileName.empty ())
        return true;

    // Restores configuration from saved state
    // Returns true if a valid saved configuration is
    //   available, and false if no saved config is
    //   stored, or if there has been a problem
    U2ConfigFile cfg;

    try {
        // Don't trim whitespace
        cfg.load(mConfigFileName, "\t:=", false);
    }
    catch (U2Exception& e)
    {
        if (e.getNumber() == U2Exception::ERR_FILE_NOT_FOUND)
        {
            return false;
        }
        else
        {
            throw;
        }
    }

    U2ConfigFile::SectionIterator iSection = cfg.getSectionIterator();
    while (iSection.hasMoreElements())
    {
        const U2String& renderSystem = iSection.peekNextKey();
        const U2ConfigFile::SettingsMultiMap& settings = *iSection.getNext();

        U2RenderSystem* rs = getRenderSystemByName(renderSystem);
        if (!rs)
        {
            // Unrecognised render system
            continue;
        }

        U2ConfigFile::SettingsMultiMap::const_iterator i;
        for (i = settings.begin(); i != settings.end(); ++i)
        {
            rs->setConfigOption(i->first, i->second);
        }
    }

    U2RenderSystem* rs = getRenderSystemByName(cfg.getSetting("Render System"));
    if (!rs)
    {
        // Unrecognised render system
        return false;
    }

    U2String err = rs->validateConfigOptions();
    if (err.length() > 0)
        return false;

    setRenderSystem(rs);

    // Successful load
    return true;
}

//-----------------------------------------------------------------------
const U2RenderSystemList& U2Root::getAvailableRenderers(void)
{
    return mRenderers;
}

//-----------------------------------------------------------------------
U2RenderSystem* U2Root::getRenderSystemByName(const U2String& name)
{
    if (name.empty())
    {
        // No render system
        return NULL;
    }

    U2RenderSystemList::const_iterator pRend;
    for (pRend = getAvailableRenderers().begin(); pRend != getAvailableRenderers().end(); ++pRend)
    {
        U2RenderSystem* rs = *pRend;
        if (rs->getName() == name)
            return rs;
    }

    // Unrecognised render system
    return NULL;
}

//-----------------------------------------------------------------------
void U2Root::setRenderSystem(U2RenderSystem* system)
{
    // Sets the active rendering system
    // Can be called direct or will be called by
    //   standard config dialog

    // Is there already an active renderer?
    // If so, disable it and init the new one
    if( mActiveRenderer && mActiveRenderer != system )
    {
        mActiveRenderer->shutdown();
    }

    mActiveRenderer = system;
    // Tell scene managers
//    U2SceneManagerEnumerator::getSingleton().setRenderSystem(system);
}

//-----------------------------------------------------------------------
void U2Root::addRenderSystem(U2RenderSystem *newRend)
{
    mRenderers.push_back(newRend);
}

//-----------------------------------------------------------------------
U2RenderSystem* U2Root::getRenderSystem(void)
{
    return mActiveRenderer;
}

U2Timer* U2Root::getTimer(void)
{
    return mTimer;
} 
//-----------------------------------------------------------------------
void U2Root::addFrameListener(U2FrameListener* newListener)
{
    // Check if the specified listener is scheduled for removal
    std::set<U2FrameListener *>::iterator i = mRemovedFrameListeners.find(newListener);

    // If yes, cancel the removal. Otherwise add it to other listeners.
    if (i != mRemovedFrameListeners.end())
        mRemovedFrameListeners.erase(*i);
    else
        mFrameListeners.insert(newListener); // Insert, unique only (set)
}

//-----------------------------------------------------------------------
void U2Root::removeFrameListener(U2FrameListener* oldListener)
{
    // Remove, 1 only (set), and only when this listener was added before.
    if( mFrameListeners.find( oldListener ) != mFrameListeners.end() )
        mRemovedFrameListeners.insert(oldListener);
}
//-----------------------------------------------------------------------
void U2Root::startRendering(void)
{
    assert(mActiveRenderer != 0);

    mActiveRenderer->_initRenderTargets();

    // Clear event times
    clearEventTimes();

    // Infinite loop, until broken out of by frame listeners
    // or break out by calling queueEndRendering()
    mQueuedEnd = false;

    while( !mQueuedEnd )
    {
        //Pump messages in all registered U2RenderWindow windows
        U2WindowEventUtilities::messagePump();

        if (!renderOneFrame())
            break;
    }
}
//-----------------------------------------------------------------------
bool U2Root::renderOneFrame(void)
{
    if(!_fireFrameStarted())
        return false;

    if (!_updateAllRenderTargets())
        return false;

    return _fireFrameEnded();
}
//---------------------------------------------------------------------
bool U2Root::renderOneFrame(u2real timeSinceLastFrame)
{
    FrameEvent evt;
    evt.timeSinceLastFrame = timeSinceLastFrame;

    unsigned long now = mTimer->getMilliseconds();
    evt.timeSinceLastEvent = calculateEventTime(now, FETT_ANY);

    if(!_fireFrameStarted(evt))
        return false;

    if (!_updateAllRenderTargets(evt))
        return false;

    now = mTimer->getMilliseconds();
    evt.timeSinceLastEvent = calculateEventTime(now, FETT_ANY);

    return _fireFrameEnded(evt);
}
//-----------------------------------------------------------------------
bool U2Root::_fireFrameStarted(FrameEvent& evt)
{
    /**********************#####################
    OgreProfileBeginGroup("Frame", OGREPROF_GENERAL);
    */

    // Remove all marked listeners
    std::set<U2FrameListener*>::iterator i;
    for (i = mRemovedFrameListeners.begin();
        i != mRemovedFrameListeners.end(); i++)
    {
        mFrameListeners.erase(*i);
    }
    mRemovedFrameListeners.clear();

    // Tell all listeners
    for (i= mFrameListeners.begin(); i != mFrameListeners.end(); ++i)
    {
        if (!(*i)->frameStarted(evt))
            return false;
    }

    return true;
}
//-----------------------------------------------------------------------
bool U2Root::_fireFrameRenderingQueued(FrameEvent& evt)
{
    // Increment next frame number
    ++mNextFrame;

    // Remove all marked listeners
    std::set<U2FrameListener*>::iterator i;
    for (i = mRemovedFrameListeners.begin();
        i != mRemovedFrameListeners.end(); i++)
    {
        mFrameListeners.erase(*i);
    }
    mRemovedFrameListeners.clear();

    // Tell all listeners
    for (i= mFrameListeners.begin(); i != mFrameListeners.end(); ++i)
    {
        if (!(*i)->frameRenderingQueued(evt))
            return false;
    }

    return true;
}
//-----------------------------------------------------------------------
bool U2Root::_fireFrameEnded(FrameEvent& evt)
{
    // Remove all marked listeners
    std::set<U2FrameListener*>::iterator i;
    for (i = mRemovedFrameListeners.begin();
        i != mRemovedFrameListeners.end(); i++)
    {
        mFrameListeners.erase(*i);
    }
    mRemovedFrameListeners.clear();

    // Tell all listeners
    bool ret = true;
    for (i= mFrameListeners.begin(); i != mFrameListeners.end(); ++i)
    {
        if (!(*i)->frameEnded(evt))
        {
            ret = false;
            break;
        }
    }

    // Tell buffer manager to free temp buffers used this frame
    if (U2HardwareBufferManager::getSingletonPtr())
        U2HardwareBufferManager::getSingleton()._releaseBufferCopies();

    // Tell the queue to process responses
    mWorkQueue->processResponses();

    /**********************#####################
    OgreProfileEndGroup("Frame", OGREPROF_GENERAL);
    */

    return ret;
}
//-----------------------------------------------------------------------
bool U2Root::_fireFrameStarted()
{
    FrameEvent evt;
    populateFrameEvent(FETT_STARTED, evt);

    return _fireFrameStarted(evt);
}
//-----------------------------------------------------------------------
bool U2Root::_fireFrameRenderingQueued()
{
    FrameEvent evt;
    populateFrameEvent(FETT_QUEUED, evt);

    return _fireFrameRenderingQueued(evt);
}
//-----------------------------------------------------------------------
bool U2Root::_fireFrameEnded()
{
    FrameEvent evt;
    populateFrameEvent(FETT_ENDED, evt);
    return _fireFrameEnded(evt);
}
//---------------------------------------------------------------------
void U2Root::populateFrameEvent(FrameEventTimeType type, FrameEvent& evtToUpdate)
{
    unsigned long now = mTimer->getMilliseconds();
    evtToUpdate.timeSinceLastEvent = calculateEventTime(now, FETT_ANY);
    evtToUpdate.timeSinceLastFrame = calculateEventTime(now, type);
}
//-----------------------------------------------------------------------
u2real U2Root::calculateEventTime(unsigned long now, FrameEventTimeType type)
{
    // Calculate the average time passed between events of the given type
    // during the last mFrameSmoothingTime seconds.

    EventTimesQueue& times = mEventTimes[type];
    times.push_back(now);

    if(times.size() == 1)
        return 0;

    // Times up to mFrameSmoothingTime seconds old should be kept
    unsigned long discardThreshold =
        static_cast<unsigned long>(mFrameSmoothingTime * 1000.0f);

    // Find the oldest time to keep
    EventTimesQueue::iterator it = times.begin(),
        end = times.end()-2; // We need at least two times
    while(it != end)
    {
        if (now - *it > discardThreshold)
            ++it;
        else
            break;
    }

    // Remove old times
    times.erase(times.begin(), it);

    return u2real(times.back() - times.front()) / ((times.size()-1) * 1000);
}
//-----------------------------------------------------------------------
void U2Root::queueEndRendering(void)
{
    mQueuedEnd = true;
}
//-----------------------------------------------------------------------
void U2Root::clearEventTimes(void)
{
    // Clear event times
    for(int i = 0; i < FETT_COUNT; ++i)
        mEventTimes[i].clear();
}
//-----------------------------------------------------------------------
bool U2Root::_updateAllRenderTargets(void)
{
    // update all targets but don't swap buffers
    mActiveRenderer->_updateAllRenderTargets(false);
    // give client app opportunity to use queued GPU time
    bool ret = _fireFrameRenderingQueued();
    // block for final swap
    mActiveRenderer->_swapAllRenderTargetBuffers(mActiveRenderer->getWaitForVerticalBlank());

    /**********************#####################
    // This belongs here, as all render targets must be updated before events are
    // triggered, otherwise targets could be mismatched.  This could produce artifacts,
    // for instance, with shadows.
    for (U2SceneManagerEnumerator::SceneManagerIterator it = getSceneManagerIterator(); it.hasMoreElements(); it.moveNext())
        it.peekNextValue()->_handleLodEvents();
    */

    return ret;
}
//---------------------------------------------------------------------
bool U2Root::_updateAllRenderTargets(FrameEvent& evt)
{
    // update all targets but don't swap buffers
    mActiveRenderer->_updateAllRenderTargets(false);
    // give client app opportunity to use queued GPU time
    bool ret = _fireFrameRenderingQueued(evt);
    // block for final swap
    mActiveRenderer->_swapAllRenderTargetBuffers(mActiveRenderer->getWaitForVerticalBlank());

    /**********************#####################
    // This belongs here, as all render targets must be updated before events are
    // triggered, otherwise targets could be mismatched.  This could produce artifacts,
    // for instance, with shadows.
    for (U2SceneManagerEnumerator::SceneManagerIterator it = getSceneManagerIterator(); it.hasMoreElements(); it.moveNext())
        it.peekNextValue()->_handleLodEvents();
    */

    return ret;
}

//---------------------------------------------------------------------
void U2Root::setWorkQueue(WorkQueue* queue)
{
    if (mWorkQueue != queue)
    {
        // delete old one (will shut down)
        U2_DELETE mWorkQueue;

        mWorkQueue = queue;
        if (m_bInitialised)
            mWorkQueue->startup();
    }
}

//-----------------------------------------------------------------------
U2RenderWindow* U2Root::initialise(bool autoCreateWindow, const U2String& windowTitle, const U2String& customCapabilitiesConfig)
{
    if (!mActiveRenderer)
        U2_EXCEPT(U2Exception::ERR_INVALID_STATE,
        "Cannot initialise - no render "
        "system has been selected.", "U2Root::initialise");

    if (!mControllerManager)
        mControllerManager = U2_NEW ControllerManager();

    // .rendercaps manager
    U2RenderSystemCapabilitiesManager& rscManager = U2RenderSystemCapabilitiesManager::getSingleton();
    // caller wants to load custom U2RenderSystemCapabilities form a config file
    if(customCapabilitiesConfig != U2StringUtil::BLANK)
    {
        U2ConfigFile cfg;
        cfg.load(customCapabilitiesConfig, "\t:=", false);

        // Capabilities Database setting must be in the same format as
        // resources.cfg in Ogre examples.
        U2ConfigFile::SettingsIterator iter = cfg.getSettingsIterator("Capabilities Database");
        while(iter.hasMoreElements())
        {
            U2String archType = iter.peekNextKey();
            U2String filename = iter.getNext();

            rscManager.parseCapabilitiesFromArchive(filename, archType, true);
        }

        U2String capsName = cfg.getSetting("Custom Capabilities");
        // The custom capabilities have been parsed, let's retrieve them
        U2RenderSystemCapabilities* rsc = rscManager.loadParsedCapabilities(capsName);
        if(rsc == 0)
        {
            U2_EXCEPT(U2Exception::ERR_ITEM_NOT_FOUND,
                U2String("Cannot load a RenderSystemCapability named ") + capsName,
                "U2Root::initialise");
        }

        // Tell U2RenderSystem to use the comon rsc
        useCustomRenderSystemCapabilities(rsc);
    }


    //PlatformInformation::log(U2LogManager::getSingleton().getDefaultLog());
    mAutoWindow =  mActiveRenderer->_initialise(autoCreateWindow, windowTitle);


    if (autoCreateWindow && !mFirstTimePostWindowInit)
    {
        oneTimePostWindowInit();
        mAutoWindow->_setPrimary();
    }

    // Initialise timer
    mTimer->reset();

//     // Init pools
//     ConvexBody::_initialisePool();

    m_bInitialised = true;

    return mAutoWindow;
}

//-----------------------------------------------------------------------
void U2Root::oneTimePostWindowInit(void)
{
    if (!mFirstTimePostWindowInit)
    {
        // Background loader
        mResourceBackgroundQueue->initialise();
        mWorkQueue->startup();
//         // Initialise material manager
//         mMaterialManager->initialise();
//         // Init particle systems manager
//         mParticleManager->_initialise();
//         // Init mesh manager
//         MeshManager::getSingleton()._initialise();
        // Init plugins - after window creation so rsys resources available
        initialisePlugins();
        mFirstTimePostWindowInit = true;
    }
}
//-----------------------------------------------------------------------
void U2Root::useCustomRenderSystemCapabilities(U2RenderSystemCapabilities* capabilities)
{
    mActiveRenderer->useCustomRenderSystemCapabilities(capabilities);
}
//-----------------------------------------------------------------------
U2SceneManager* U2Root::_getCurrentSceneManager(void) const
{
    if (mSceneManagerStack.empty())
        return 0;
    else
        return mSceneManagerStack.back();
}
//-----------------------------------------------------------------------
void U2Root::_pushCurrentSceneManager(U2SceneManager* sm)
{
    mSceneManagerStack.push_back(sm);
}
//-----------------------------------------------------------------------
void U2Root::_popCurrentSceneManager(U2SceneManager* sm)
{
    assert (_getCurrentSceneManager() == sm && "Mismatched push/pop of U2SceneManager");

    mSceneManagerStack.pop_back();
}
//-----------------------------------------------------------------------
void U2Root::addSceneManagerFactory(U2SceneManagerFactory* fact)
{
    mSceneManagerEnum->addFactory(fact);
}
//-----------------------------------------------------------------------
void U2Root::removeSceneManagerFactory(U2SceneManagerFactory* fact)
{
    mSceneManagerEnum->removeFactory(fact);
}
//-----------------------------------------------------------------------
const SceneManagerMetaData* U2Root::getSceneManagerMetaData(const U2String& typeName) const
{
    return mSceneManagerEnum->getMetaData(typeName);
}
//-----------------------------------------------------------------------
U2SceneManagerEnumerator::MetaDataIterator U2Root::getSceneManagerMetaDataIterator(void) const
{
    return mSceneManagerEnum->getMetaDataIterator();
}
//-----------------------------------------------------------------------
U2SceneManager* U2Root::createSceneManager(const U2String& typeName, 
                                       const U2String& instanceName)
{
    return mSceneManagerEnum->createSceneManager(typeName, instanceName);
}
//-----------------------------------------------------------------------
U2SceneManager* U2Root::createSceneManager(SceneTypeMask typeMask, 
                                       const U2String& instanceName)
{
    return mSceneManagerEnum->createSceneManager(typeMask, instanceName);
}
//-----------------------------------------------------------------------
void U2Root::destroySceneManager(U2SceneManager* sm)
{
    mSceneManagerEnum->destroySceneManager(sm);
}
//-----------------------------------------------------------------------
U2SceneManager* U2Root::getSceneManager(const U2String& instanceName) const
{
    return mSceneManagerEnum->getSceneManager(instanceName);
}
//---------------------------------------------------------------------
bool U2Root::hasSceneManager(const U2String& instanceName) const
{
    return mSceneManagerEnum->hasSceneManager(instanceName);
}
//-----------------------------------------------------------------------
U2SceneManagerEnumerator::SceneManagerIterator U2Root::getSceneManagerIterator(void)
{
    return mSceneManagerEnum->getSceneManagerIterator();
}
